#include "LFDNode.h"
#include <osg/Object>
#include <osgViewer/Viewer>
#include <osgEarth/StringUtils>
#include <osgEarth/JsonUtils>
#include <osg/PagedLOD>
#include <osg/ProxyNode>
#include <osgEarthAnnotation/PlaceNode>
#include <QDebug>
#include <osgEarth/JsonUtils>
#include <amk3/IOUtil.h>

using namespace osgEarth;

LFDNode::LFDNode()
{
    _request.first = "";
    _request.second = 0;
    _nodeVersion = 0;
}

void LFDNode::traverse(osg::NodeVisitor &nv)
{
    if (nv.getDatabaseRequestHandler() && _nodeVersion < _lfdOption->version)
    {
        nv.getDatabaseRequestHandler()->requestNodeFile(_request.first, nv.getNodePath(),
                     1.0f, nv.getFrameStamp(), _request.second, _lfdOption.get());
    }
    else
    {
        Group::traverse(nv);
    }
}

void LFDNode::update(int v)
{
    std::string path = Stringify() << v;
    path = "LFD_" +  path + ".lfd";
    _request.first = path;
    _request.second = 0;
    _lfdOption->version = v;
}

bool LFDNode::addChild(osg::Node *child)
{
    if(!child) return false;
    int cf2 = 0;
    child->getUserValue(LFD_VERSION,cf2);
    if(this->getNumChildren() <= 0){
        _nodeVersion = cf2;
        return osg::Group::addChild(child);
    }

    osg::Node* c = this->getChild(0);
    int cf = 0;

    c->getUserValue(LFD_VERSION,cf);

    if(cf2 < cf){
        return false;
    }
    this->removeChild(0,this->getNumChildren());

    _nodeVersion = cf2;
    return osg::Group::addChild(child);
}

struct LFDNodeLoader : public osgDB::ReaderWriter
{
    LFDNodeLoader()
    {
        supportsExtension( "lfd", "lfd" );
    }

    const char* className() const
    { // override
        return "lfd";
    }

    ReadResult readNode(const std::string& uri, const osgDB::Options* readOptions) const
    {
        if ( !acceptsExtension( osgDB::getLowerCaseFileExtension(uri) ) )
            return ReadResult::FILE_NOT_HANDLED;

        const LFDOption* opt = dynamic_cast<const LFDOption*>(readOptions);
        if(opt == 0){
            return ReadResult::ERROR_IN_READING_FILE;
        }

        LFDSource* source = opt->source;
        if(source == 0){
            return ReadResult::ERROR_IN_READING_FILE;
        }
        int level = opt->level;
        osgEarth::Bounds bs = opt->bounds;
        std::string url = source->buildUrl(level,bs);
        if(url.length() <= 0) return ReadResult::FILE_NOT_FOUND;

        size_t data_length = 0;
        char* data_buffer = 0;

		IOUtil::get(url, data_length, &data_buffer);


        if(data_length <= 0 || data_buffer ==0){
            return ReadResult::ERROR_IN_READING_FILE;
        }

        osg::Group* g = new osg::Group();
        g->setUserValue(LFD_VERSION,opt->version);
        source->parseNode(data_buffer,data_length,g,opt);
        return g;
    }

};

REGISTER_OSGPLUGIN(lfd, LFDNodeLoader);


LFDSource::LFDSource()
{

}

LFDSource::~LFDSource()
{

}

void LFDSource::open()
{
    if(_rootNode != 0) return;
    osgViewer::Viewer* pv = getSceneContext()->getViewer();
    if(_eventHandler == 0){

        _eventHandler = new LFDEventHandler();
        _eventHandler->_source = this;
        pv->addEventHandler(_eventHandler);
    }
    _rootNode = new osg::Group();

    for(int i=0; i<_levels.size(); i++){
        LFDLevel& lv = _levels.at(i);
        LFDNode* node = new LFDNode();
        LFDOption* option = new LFDOption();
        option->bounds = this->getBounds();
        option->level = i;
        option->source = this;
        node->setDatabaseOptions(option);
        _rootNode->addChild(node);
        _lfdNodes.push_back(node);
    }
	getSceneContext()->getMapSceneNode()->addChild(_rootNode);
    this->_visible = true;
    this->update();
}

void LFDSource::close()
{
    if(_rootNode){
        _rootNode->removeChildren(0,_rootNode->getNumChildren());
		getSceneContext()->getMapSceneNode()->removeChild(_rootNode);
        _lfdNodes.clear();

		osgViewer::Viewer* pv = getSceneContext()->getViewer();
        pv->removeEventHandler(_eventHandler);

        _rootNode = 0;
        _eventHandler = 0;
    }
}

void LFDSource::update()
{
    if(!getVisible()) return;
    static osg::Vec3 laste;

    osg::Vec3 e,c,u;
    double lat,lon,hei;
	getSceneContext()->getCamera()->getViewMatrixAsLookAt(e, c, u, 1000);

    if(laste == e) return;
    laste = e;

	getSceneContext()->getEllipsoidModel()->convertXYZToLatLongHeight(e.x(), e.y(), e.z(), lat, lon, hei);



    for(int i=0;i<_lfdNodes.size();i++){
        LFDNode* n = _lfdNodes.at(i);
        n->setNodeMask(0);
    }

    LFDNode* node = 0;
    LFDLevel* level = 0;
    for(int i=0;i<_lfdNodes.size();i++){
        LFDLevel& lv = _levels.at(i);
        if(lv.min < hei && lv.max >hei){
            node = _lfdNodes.at(i);
            level = &lv;
            break;
        }
    }


    if(node == 0) return;

    static int g_version = 1;
    node->setNodeMask(1);

    if(!level->isPaged)  {
        if(node->getNodeVersion() <= 0){
            node->update(g_version++);
        }
        return;
    };
    int ver = g_version++;
    double llradius = hei / 200000;
    lon = osg::RadiansToDegrees(lon);
    lat =osg::RadiansToDegrees(lat);
    osgEarth::Bounds bs(lon - llradius,lat-llradius,lon + llradius,lat+llradius);

    static osgEarth::Bounds last_bs;
    static LFDLevel* last_lv;
    if(last_lv == level){
        if(last_bs.xMin() <= bs.xMin()&&
           last_bs.yMin() <= bs.yMin()&&
           last_bs.xMax() >= bs.xMax()&&
           last_bs.yMax() >= bs.yMax()){
            return;
        }
    }

    last_bs = bs;
    last_lv = level;

    //node->getDatabaseOptions()->version = ver;
    node->getDatabaseOptions()->bounds = bs;
    node->update(ver);
}


bool LFDSource::parseNode(const char* data,int length,osg::Group* parent,const LFDOption* option)
{
    return false;
}

std::string LFDSource::buildUrl(int lv, Bounds &bs)
{
    return "";
}

void LFDSource::addLevel(float min, float max, bool paged)
{
    LFDLevel level;
    level.min = min;
    level.max = max;
    level.isPaged = paged;
    _levels.push_back(level);
}

void LFDSource::setVisible(bool b)
{
    if(_rootNode){
        _visible = b;
        _rootNode->setNodeMask(b ? 1 : 0 );
    }
}


LFDOption::LFDOption()
{
    version = 0;
    source =0;
}

LFDOption::LFDOption(const LFDOption &o, const osg::CopyOp &copyop)
    :osgDB::Options(o, copyop)
{
    this->level = o.level;
    this->bounds= o.bounds;
    this->source = o.source;
    this->version = o.version;
}


bool LFDEventHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
    if(!_source) return false;
    if(ea.getEventType() == ea.RELEASE && ea.getButton()& ea.LEFT_MOUSE_BUTTON){
        _source->update();
        return false;
    }
    if(ea.getEventType() == ea.SCROLL){
        _source->update();
        return false;
    }
    return false;
}
